﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Newtonsoft.Json;
using System.Threading;

namespace BasePlatformNetAgent
{
	public static class NamedPipeNative
	{
		public const uint PIPE_ACCESS_DUPLEX = 0x00000003;

		public const uint PIPE_READMODE_MESSAGE = 0x00000002;

		public const uint PIPE_TYPE_MESSAGE = 0x00000004;

		public const uint PIPE_WAIT = 0x00000000;

		public const uint PIPE_UNLIMITED_INSTANCES = 255;

		public const int INVALID_HANDLE_VALUE = -1;

		public const ulong ERROR_PIPE_CONNECTED = 535;

		public const uint GENERIC_WRITE = (0x40000000);

		public const uint GENERIC_READ = (0x80000000);

		public const uint OPEN_EXISTING = 3;

		public const ulong ERROR_PIPE_BUSY = 231;

		[DllImport("kernel32.dll", SetLastError = true)]
		public static extern IntPtr CreateFile(
			String lpFileName,						// file name
			uint dwDesiredAccess,					// access mode
			uint dwShareMode,						// share mode
			SecurityAttributes attr,				// SD
			uint dwCreationDisposition,				// how to create
			uint dwFlagsAndAttributes,				// file attributes
			uint hTemplateFile);

		[DllImport("kernel32.dll", SetLastError = true)]
		public static extern IntPtr CreateNamedPipe(
			String lpName,							// pipe name
			uint dwOpenMode,						// pipe open mode
			uint dwPipeMode,						// pipe-specific modes
			uint nMaxInstances,						// maximum number of instances
			uint nOutBufferSize,					// output buffer size
			uint nInBufferSize,						// input buffer size
			uint nDefaultTimeOut,					// time-out interval
			IntPtr pipeSecurityDescriptor			// SD
			);

		[DllImport("kernel32.dll", SetLastError = true)]
		public static extern bool ConnectNamedPipe(
			IntPtr hHandle,							// handle to named pipe
			Overlapped lpOverlapped					// overlapped structure
			);

		[DllImport("kernel32.dll", SetLastError = true)]
		public static extern bool ReadFile(
			IntPtr hHandle,							// handle to file
			byte[] lpBuffer,						// data buffer
			uint nNumberOfBytesToRead,				// number of bytes to read
			byte[] lpNumberOfBytesRead,				// number of bytes read
			uint lpOverlapped						// overlapped buffer
			);

		[DllImport("kernel32.dll", SetLastError = true)]
		public static extern bool WriteFile(
			IntPtr hHandle,							// handle to file
			byte[] lpBuffer,						// data buffer
			uint nNumberOfBytesToWrite,				// number of bytes to write
			byte[] lpNumberOfBytesWritten,			// number of bytes written
			uint lpOverlapped						// overlapped buffer
			);

		[DllImport("kernel32.dll", SetLastError = true)]
		public static extern bool FlushFileBuffers(IntPtr hHandle);

		[DllImport("kernel32.dll", SetLastError = true)]
		public static extern bool DisconnectNamedPipe(IntPtr hHandle);

		[DllImport("kernel32.dll", SetLastError = true)]
		public static extern bool SetNamedPipeHandleState(
			IntPtr hHandle,
			ref uint mode,
			IntPtr cc,
			IntPtr cd);

		[DllImport("kernel32.dll", SetLastError = true)]
		public static extern bool WaitNamedPipe(
			String name,
			int timeout);

		[DllImport("kernel32.dll", SetLastError = true)]
		public static extern bool CloseHandle(IntPtr hHandle);

		[DllImport("kernel32.dll", SetLastError = true)]
		public static extern uint GetLastError();

		// 发送消息
		public static void SendMsg(string pipeName, string msg)
		{
			byte[] msgBuffer = Encoding.UTF8.GetBytes(msg);

			IntPtr fileHandle;
			int busyCount = 0;
			int waitCount = 0;
			while (true)
			{
				fileHandle = NamedPipeNative.CreateFile(pipeName,
					NamedPipeNative.GENERIC_READ | NamedPipeNative.GENERIC_WRITE,
					0, null, NamedPipeNative.OPEN_EXISTING, 0, 0);

				if (fileHandle.ToInt32() != NamedPipeNative.INVALID_HANDLE_VALUE)
				{
					//Console.WriteLine("Connect Success!");
					break;
				}
				if (NamedPipeNative.GetLastError() == NamedPipeNative.ERROR_PIPE_BUSY)
				{
					Thread.Sleep(1);
					busyCount++;
					if (busyCount > 10)
					{
						//Console.WriteLine("Busy time > 10, Connect Failed!");
						break;
					}
					continue;
				}
				if (!NamedPipeNative.WaitNamedPipe(pipeName, 10))
				{
					waitCount++;
					if (waitCount > 1)
					{
						//Console.WriteLine("Wait time > 10, Connect Failed!");
						break;
					}
					else
					{
						continue;
					}
				}

				uint pipeMode = NamedPipeNative.PIPE_READMODE_MESSAGE;
				bool setResult = NamedPipeNative.SetNamedPipeHandleState(fileHandle, ref pipeMode, IntPtr.Zero, IntPtr.Zero);

				if (!setResult)
				{
					//Console.WriteLine("设置管道状态失败, Retry");
					continue;
				}
			}

			uint len = (uint)msgBuffer.Length;
			byte[] numReadWritten = new byte[4];
			bool success = NamedPipeNative.WriteFile(fileHandle, BitConverter.GetBytes(len), 4, numReadWritten, 0);

			if (!success)
			{
				Console.WriteLine("写入数据失败, Return");
				return;
			}
			else
			{
				NamedPipeNative.WriteFile(fileHandle, msgBuffer, len, numReadWritten, 0);
			}
			
			NamedPipeNative.CloseHandle(fileHandle);
		}

		// 发送消息
		public static void SendMsg(string pipeName, ShareMessage msg, out ShareMessage response, bool useJson = false)
		{
			response = null;

			byte[] msgBuffer = null;

			if (useJson == false)
			{
				msgBuffer = BytesConvert.ObjectToBytes<ShareMessage>(msg);
			}
			else
			{
				msgBuffer = System.Text.Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(msg));
			}

			IntPtr fileHandle = IntPtr.Zero;
			int busyCount = 0;
			int waitCount = 0;
			int sendRetryCount = 0;
			
			while (sendRetryCount < 3)
			{
				while (true)
				{
					fileHandle = NamedPipeNative.CreateFile(pipeName,
						NamedPipeNative.GENERIC_READ | NamedPipeNative.GENERIC_WRITE,
						0, null, NamedPipeNative.OPEN_EXISTING, 0, 0);

					if (fileHandle.ToInt32() != NamedPipeNative.INVALID_HANDLE_VALUE)
					{
						//Console.WriteLine("Connect Success!");
						break;
					}
					if (NamedPipeNative.GetLastError() == NamedPipeNative.ERROR_PIPE_BUSY)
					{
						Thread.Sleep(1);
						busyCount++;
						if (busyCount > 10)
						{
							//Console.WriteLine("Busy time > 10, Connect Failed!");
							break;
						}
						else
						{
							//Console.WriteLine("NamedPipeNative.ERROR_PIPE_BUSY, busyCount = " + busyCount);
							continue;
						}
					}
					if (!NamedPipeNative.WaitNamedPipe(pipeName, 10))
					{
						waitCount++;
						if (waitCount > 1)
						{
							//Console.WriteLine("Wait time > 10, Connect Failed!");
							break;
						}
						else
						{
							continue;
						}
					}

					uint pipeMode = NamedPipeNative.PIPE_READMODE_MESSAGE;
					bool setResult = NamedPipeNative.SetNamedPipeHandleState(fileHandle, ref pipeMode, IntPtr.Zero, IntPtr.Zero);

					if (!setResult)
					{
						//Console.WriteLine("设置管道状态失败, Retry");
						continue;
					}
				}

				uint len = (uint)msgBuffer.Length;
				byte[] numReadWritten = new byte[4];
				bool success = NamedPipeNative.WriteFile(fileHandle, BitConverter.GetBytes(len), 4, numReadWritten, 0);

				if (!success)
				{
					//Console.WriteLine("写入数据失败, Return");
					//response = null;
					//return;
					sendRetryCount++;
					NamedPipeNative.CloseHandle(fileHandle);
					Thread.Sleep(2);
					continue;
				}
				else
				{
					NamedPipeNative.WriteFile(fileHandle, msgBuffer, len, numReadWritten, 0);

					#region 接收反馈消息

					byte[] intBytes = new byte[4];

					// 获取反馈消息长度
					NamedPipeNative.ReadFile(fileHandle, intBytes, 4, numReadWritten, 0);
					int responseLen = BitConverter.ToInt32(intBytes, 0);

					// 获取反馈消息数据
					byte[] recBuffer = new byte[responseLen];
					NamedPipeNative.ReadFile(fileHandle, recBuffer, (uint)responseLen, numReadWritten, 0);

					// 解析反馈消息内容
					if (useJson == false)
					{
						response = BytesConvert.BytesToObject<ShareMessage>(recBuffer);
					}
					else
					{
						response = JsonConvert.DeserializeObject<ShareMessage>(System.Text.Encoding.UTF8.GetString(recBuffer));
					}

					#endregion

					NamedPipeNative.CloseHandle(fileHandle);

					break;
				}

				//NamedPipeNative.CloseHandle(fileHandle);
			}

			if (sendRetryCount >= 3)
			{
				Console.WriteLine("写入数据失败, Return");
				response = null;
				return;
			}
		}
	}

	[StructLayout(LayoutKind.Sequential)]
	public class Overlapped
	{
	}

	[StructLayout(LayoutKind.Sequential)]
	public class SecurityAttributes
	{
	}
}
